---
title: Performing side effects
version: 1
---

import { Link } from "../../src/components/Link";
import { AutoSnippet, When } from "../../src/components/CodeSnippet";
import Legend, { colors } from "./first_request/legend/legend";
import todoListProvider from "./side_effects/todo_list_provider";
import todoListNotifier from "./side_effects/todo_list_notifier";
import todoListNotifierAddTodo from "./side_effects/todo_list_notifier_add_todo";
import consumerAddTodoCall from "!!raw-loader!./side_effects/raw/consumer_add_todo_call.dart";
import restAddTodo from "!!raw-loader!./side_effects/raw/rest_add_todo.dart";
import invalidateSelfAddTodo from "!!raw-loader!./side_effects/raw/invalidate_self_add_todo.dart";
import manualAddTodo from "!!raw-loader!./side_effects/raw/manual_add_todo.dart";
import mutableManualAddTodo from "!!raw-loader!./side_effects/raw/mutable_manual_add_todo.dart";
import renderAddTodo from "./side_effects/render_add_todo";

So far, we've only seen how to fetch data (aka perform a _GET_ HTTP request).  
But what about side-effects, such as a _POST_ request?

Applications often implement a CRUD (Create, Read, Update, Delete) API.  
When doing so, it is common that an update request (typically a _POST_) should
also update the local cache to have the UI reflect the new state.

The problem is, how do we update the state of a provider from within a consumer?  
Naturally, providers do not expose a way to modify their state.
This is by design, to ensure that the state is only modified in a controlled way
and promote separation of concerns.  
Instead, providers have to explicitly expose a way to modify their state.

To do that, we will use a new concept: Notifiers.  
To showcase this new concept, let's use a more advanced example: A to-do list.

## Defining a Notifier

Let's start with what we already know by this point: A plain simple _GET_ request.
As saw previously in <Link documentID="essentials/first_request" />, we could
fetch a list of todos by writing:

<AutoSnippet {...todoListProvider} />

Now that we've fetch a list of todos, let's see how we can add a new todos.  
For this, we will need to modify our provider such that they expose a public API
for modifying their state. This is done by converting our provider into what
we call a "notifier".

Notifiers are the "stateful widget" of providers. They require a slight tweak to
the syntax for defining a provider.  
This new syntax is as follows:

<When codegen={false}>
<Legend
  code={`final name = SomeNotifierProvider.someModifier<MyNotifier, Result>(MyNotifier.new);
 
class MyNotifier extends SomeNotifier<Result> {
  @override
  Result build() {
    <your logic here>
  }

  <your methods here>
}`}
  annotations={[
    {
      offset: 6,
      length: 4,
      label: "The provider variable",
       description: <>

This variable is what will be used to interact with our provider.  
The variable must be final and "top-level" (global).

</>
    },
    {
      offset: 13,
      length: 20,
      label: "The provider type",
       description: <>

Generally either `NotifierProvider`, `AsyncNotifierProvider` or `StreamNotifierProvider`.  
The type of provider used depends on the return value of your function.
For example, to create a `Future<Activity>`, you'll want a `AsyncNotifierProvider<Activity>`.

`AsyncNotifierProvider` is the one you'll want to use the most.

:::tip
Don't think in terms of "Which provider should I pick".
Instead, think in terms of "What do I want to return". The provider type
will follow naturally.
:::

</>
    },
    {
      offset: 33,
      length: 13,
      label: "Modifiers (optional)",
      description: <>

Often, after the type of the provider you may see a "modifier".  
Modifiers are optional, and are used to tweak the behavior of the provider
in a way that is type-safe.

There are currently two modifiers available:

- `autoDispose`, which will automatically clear the cache when the provider
  stops being used.  
  See also <Link documentID="essentials/auto_dispose" />
- `family`, which enables passing arguments to your provider.  
  See also <Link documentID="essentials/passing_args" />.

</>
    },
    {
      offset: 67,
      length: 14,
      label: "The Notifier's constructor",
      description: <>

The parameter of "notifier providers" is a function which is expected
to instantiate the "notifier".  
It generally should be a "constructor tear-off".

</>
    },
    {
      offset: 86,
      length: 16,
      label: "The Notifier",
      description: <>

If `NotifierProvider` is the "StatefulWidget" class, then this part is
the `State` class.

This class is responsible for exposing ways to modify the state of the provider.  
Public methods on this class are accessible to consumers using `ref.read(yourProvider.notifier).yourMethod()`.

:::note
Notifiers should not have public properties besides the built-in `state`, as the UI
would have no mean to know that state has changed.
:::

</>
    },
    {
      offset: 111,
      length: 12,
      label: "The Notifier type",
      description: <>

The base class extended by your notifier should match that of the provider + modifiers.
Some examples would be:

- <span style={{ color: colors[0] }}>Notifier</span>Provider -> <span style={{ color: colors[0] }}>Notifier</span>
- <span style={{ color: colors[0] }}>AsyncNotifier</span>Provider -> <span style={{ color: colors[0] }}>AsyncNotifier</span>
- <span style={{ color: colors[0] }}>AsyncNotifier</span>Provider.
  <span style={{ color: colors[1] }}>autoDispose</span> -> <span
    style={{ color: colors[1] }}
  >
    AutoDispose
  </span>
  <span style={{ color: colors[0] }}>AsyncNotifier</span>
- <span style={{ color: colors[0] }}>AsyncNotifier</span>Provider.
  <span style={{ color: colors[1] }}>autoDispose</span>.<span
    style={{ color: colors[2] }}
  >
    family
  </span> -> <span style={{ color: colors[1] }}>AutoDispose</span>
  <span style={{ color: colors[2] }}>Family</span>
  <span style={{ color: colors[0] }}>AsyncNotifier</span>

To make this simpler, it is recommended to use the code generator, as it
automatically infers the correct type.

</>
    },
    {
      offset: 136,
      length: 54,
      label: "The build method",
      description: <>

All notifiers must override the `build` method.  
This method is equivalent to the place where you would normally put your
logic in a non-notifier provider.

This method should not be called directly.

</>
    },
]}
/>
</When>

<!-- Some separation for good mesure -->

<When codegen={true}>
<Legend
  code={`@riverpod
class MyNotifier extends _$MyNotifier {
  @override
  Result build() {
    <your logic here>
  }

  <your methods here>
}`}
  annotations={[
    {
      offset: 0,
      length: 9,
      label: "The annotation",
      description: <>

All providers must be annotated with `@riverpod` or `@Riverpod()`.
This annotation can be placed on global functions or classes.  
Through this annotation, it is possible to configure the provider.

For example, we can disable "auto-dispose" (which we will see later) by writing `@Riverpod(keepAlive: true)`.

</>
    },
    {
      offset: 10,
      length: 16,
      label: "The Notifier",
       description: <>

When a `@riverpod` annotation is placed on a class, that class is called
a "Notifier".  
The class must extend `_$NotifierName`, where `NotifierName` is class name.

Notifiers are responsible for exposing ways to modify the state of the provider.  
Public methods on this class are accessible to consumers using `ref.read(yourProvider.notifier).yourMethod()`.

:::note
Notifiers should not have public properties besides the built-in `state`, as the UI
would have no mean to know that state has changed.
:::

</>
    },
    {
      offset: 52,
      length: 54,
      label: "The build method",
      description: <>

All notifiers must override the `build` method.  
This method is equivalent to the place where you would normally put your
logic in a non-notifier provider.

This method should not be called directly.

</>
    },
]}
/>
</When>

For reference, you might want to check <Link documentID="essentials/first_request" />
to compare this new syntax with the previously seen syntax.

:::info
A Notifier with no method outside of `build` is identical to using the
previously seen syntax.  
The syntax shown in <Link documentID="essentials/first_request" /> can be considered
as a shorthand for notifiers with no way to be modified from the UI.
:::

Now that we've seen the syntax, let's see how to convert our previously
defined provider to a notifier:

<AutoSnippet {...todoListNotifier} />

Note that the way of reading the provider inside widgets is unchanged.  
You can still use `ref.watch(todoListProvider)` as with the previous syntax.

## Exposing a method to perform a _POST_ request

Now that we have a Notifier, we can start adding methods to enable
performing side-effects.
One such side-effect would be to have the client _POST_ a new todo.
We could do so by adding an `addTodo` method on our notifier:

<AutoSnippet {...todoListNotifierAddTodo} />

Then, we can invoke this method in our UI using the same `Consumer`/`ConsumerWidget`
we saw in <Link documentID="essentials/first_request" />:

<AutoSnippet raw={consumerAddTodoCall} />

:::info
Notice how we are using `ref.read` instead of `ref.watch` to invoke our method.  
Although `ref.watch` could technically work, it is recommended to use `ref.read`
when logic is performed in event handlers such as "onPressed".
:::

We now have a button which makes a _POST_ request when pressed.  
However, at the moment, our UI does not update to reflect the new todo list.
We will want our local cache to match the server's state.

There are a few ways to do so with their pros and cons.

### Updating our local cache to match the API response

A common backend practice is to have the _POST_ request return the new
state of the resource.  
In particular, our API would return the new list of todos after adding
a new todo. One way of doing this is by writing `state = AsyncData(response)`:

<AutoSnippet raw={restAddTodo} />

:::tip pros

- The UI will have the most up-to-date state possible.
  If another user added a todo, we will see it too.
- The server is the source of truth.
  With this approach, the client doesn't need to know where the new todo
  needs to be inserted in the list of todos.
- Only a single network-request is needed.

:::

:::danger cons

- This approach will only work if the server is implemented in a specific way.
  If the server does not return the new state, this approach will not work.
- May still not be doable if the associated _GET_ request is more complex,
  such as if it has filters/sorting.

:::

### Using `ref.invalidateSelf()` to refresh the provider.

One option is to have our provider re-execute the _GET_ request.  
This can be done by calling `ref.invalidateSelf()` after the _POST_ request:

<AutoSnippet raw={invalidateSelfAddTodo} />

:::tip pros

- The UI will have the most up-to-date state possible.
  If another user added a todo, we will see it too.
- The server is the source of truth.
  With this approach, the client doesn't need to know where the new todo
  needs to be inserted in the list of todos.
- This approach should work regarless of the server implementation.
  It can be especially useful if your _GET_ request is more complex,
  such as if it has filters/sorting.

:::

:::danger cons

- This approach will perform an extra _GET_ request, which may be
  inefficient.

:::

### Updating the local cache manually

Another option is to update the local cache manually.  
This would involve trying to mimick the backend's behavior.
For instance, we would need to know whether the backend inserts new items
at the start or at the end.

<AutoSnippet raw={manualAddTodo} />

:::info
This example uses immutable state. This is not required, but recommended.
See <Link documentID="concepts/why_immutability" /> for more details.  
If you want to use mutable state instead, you can alternatively do:

<AutoSnippet raw={mutableManualAddTodo} />

:::

:::tip pros

- This approach should work regarless of the server implementation.
- Only a single network-request is needed.

:::

:::danger cons

- The local cache may not match the server's state.
  If another user added a todo, we will not see it.
- This approach may be more complex to implement and effectively
  duplicate the backend's logic.

:::

## Going further: Showing a spinner & error handling

With all we've seen so far, we have a button which makes a _POST_ request
when pressed; and when the request is done, the UI updates to reflect
changes.  
But at the moment, there is no indication that the request is being
performed, nor any information if failed.

One way to do so is to store the Future returned by `addTodo`
in the local widget state, and then listen to that future to show
a snipper or error message.  
This is one scenario where [flutter_hooks](https://pub.dev/packages/flutter_hooks)
comes in handy. But of course, you can also use a `StatefulWidget` instead.

The following snippet shows a progress indicator while
and operation is pending. And if it failed, renders the button as red:

![A button which turns red when the operation failed](/img/essentials/side_effects/spinner.gif)

<AutoSnippet {...renderAddTodo} />
